/**
 * Copyright (C) 2010 - 2013 Harry Glasgow
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.jaden.ui.dialog.projectdefinition;

import com.googlecode.jaden.common.config.IOConfiguration;
import com.googlecode.jaden.common.config.ProjectConfiguration;
import com.googlecode.jaden.common.enums.IOType;
import com.googlecode.jaden.common.other.Utils;
import com.googlecode.jaden.ui.UiUtils;
import static com.googlecode.jaden.ui.Constants.*;
import com.googlecode.jaden.ui.UI;
import com.googlecode.jaden.ui.dialog.BaseDialog;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.event.*;
import java.util.prefs.Preferences;
import java.util.Date;
import java.text.SimpleDateFormat;

public class EditProjectDefinitionDialog extends BaseDialog {

    private final UI ui;
    private final SpinnerNumberModel numberOfInputsSpinnerModel;
    private final DefaultComboBoxModel<String> selectedInputComboModel;
    private final JComboBox<String> selectedInputCombo;
    private final JComboBox<IOType> inputTypeCombo;
    private final JTextField inputNameField;
    private final JComboBox<IOType> outputTypeCombo;
    private final JTextField outputNameField;
    private final IOConfiguration[] inputConfigurations = new IOConfiguration[99];
    private IOConfiguration outputConfiguration = new IOConfiguration(true);

    public EditProjectDefinitionDialog(UI ui, Preferences preferences,
                                       ProjectConfiguration project) {
        super(ui, DIALOG_PROJECT_DEFINITION_TITLE);
        this.ui = ui;

        numberOfInputsSpinnerModel = new SpinnerNumberModel(1, 1, 999, 1);
        JSpinner numberOfInputsSpinner = new JSpinner(numberOfInputsSpinnerModel);
        numberOfInputsSpinnerModel.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                defaultNames();
            }
        });

        selectedInputComboModel = new DefaultComboBoxModel<String>();
        selectedInputCombo = new JComboBox<String>(selectedInputComboModel);

        inputTypeCombo = new JComboBox<IOType>();
        inputTypeCombo.addItem(IOType.Numeric);
        inputTypeCombo.addItem(IOType.Map);
        inputTypeCombo.addItem(IOType.Categorical);
        inputTypeCombo.addItem(IOType.Date);

        inputNameField = new JTextField(20);

        JButton configureInputBtn = new JButton(Utils.translate(LABEL_CONFIGURE_TEXT));
        configureInputBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                configureInput();
            }
        });

        outputNameField = new JTextField(20);

        outputTypeCombo = new JComboBox<IOType>();
        outputTypeCombo.addItem(IOType.Numeric);
        outputTypeCombo.addItem(IOType.Categorical);
        outputTypeCombo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                updateOutputType();
            }
        });

        numberOfInputsSpinner.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                updateInputsCombo();
            }
        });

        JButton configureOutputBtn = new JButton(Utils.translate(LABEL_CONFIGURE_TEXT));
        configureOutputBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                configureOutput();
            }
        });

        JLabel numberOfInputsLabel = new JLabel(Utils.translate(LABEL_NUMBER_OF_INPUT_TEXT));
        JLabel selectedInputLabel = new JLabel(Utils.translate(LABEL_SELECTED_INPUT_TEXT));
        JLabel inputTypeLabel = new JLabel(Utils.translate(LABEL_INPUT_TYPE_TEXT));
        JLabel inputNameLabel = new JLabel(Utils.translate(LABEL_INPUT_NAME_TEXT));
        JLabel outputTypeLabel = new JLabel(Utils.translate(LABEL_OUTPUT_TYPE_TEXT));
        JLabel outputNameLabel = new JLabel(Utils.translate(LABEL_OUTPUT_NAME_TEXT));

        UiUtils.enWidth(numberOfInputsLabel, selectedInputLabel, inputNameLabel,
                inputTypeLabel, outputNameLabel, outputTypeLabel);

        JPanel mainPanel = UiUtils.verticalList(UiUtils.horizontalList(numberOfInputsLabel,
                numberOfInputsSpinner),
                UiUtils.horizontalList(selectedInputLabel, selectedInputCombo),
                UiUtils.horizontalList(inputNameLabel, inputNameField),
                UiUtils.horizontalList(inputTypeLabel, inputTypeCombo, configureInputBtn),
                new JSeparator(SwingConstants.HORIZONTAL),
                UiUtils.horizontalList(outputNameLabel, outputNameField),
                UiUtils.horizontalList(outputTypeLabel, outputTypeCombo, configureOutputBtn));

        int length = project.getInputColumnMappings().length;
        for (int i = 0; i < 99; i++) {
            if (i < length) {
                inputConfigurations[i] = project.getInputConfigurations()[i].copy();
            } else {
                inputConfigurations[i] = new IOConfiguration(false);
            }
        }

        numberOfInputsSpinnerModel.setValue(project.getInputConfigurations().length);
        updateInputsCombo();

        outputConfiguration = project.getOutputConfiguration().copy();
        outputNameField.setText(outputConfiguration.getName());
        outputTypeCombo.setSelectedItem(project.getOutputConfiguration().getType());

        inputTypeCombo.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                updateInputType();
            }
        });

        selectedInputCombo.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                updateInputs();
            }
        });

        inputNameField.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                adjustSelectedInputName();
            }
        });

        updateInputs();

        setMainPanel(mainPanel, preferences);
    }

    private void updateInputs() {
        int selectedInput = selectedInputCombo.getSelectedIndex();
        if (selectedInput > -1) {
            inputNameField.setText(inputConfigurations[selectedInput].getName());
            inputTypeCombo.setSelectedItem(inputConfigurations[selectedInput].getType());
        }
    }

    private void updateInputsCombo() {
        int numberOfInputs = numberOfInputsSpinnerModel.getNumber().intValue();
        while (selectedInputComboModel.getSize() > numberOfInputs) {
            selectedInputComboModel.removeElementAt(selectedInputComboModel.getSize() - 1);
        }
        while (selectedInputComboModel.getSize() < numberOfInputs) {
            selectedInputComboModel.addElement(String.valueOf(selectedInputComboModel.getSize() + 1));
        }
    }

    private void updateInputType() {
        int selectedInput = selectedInputCombo.getSelectedIndex();
        inputConfigurations[selectedInput].setType((IOType) inputTypeCombo.getSelectedItem());
    }

    private void updateOutputType() {
        outputConfiguration.setType((IOType) outputTypeCombo.getSelectedItem());
    }

    protected void doOk() {
        for (IOConfiguration configuration : inputConfigurations) {
            if (configuration.getType() == IOType.Categorical) {
                if (configuration.getCategoricalList().isEmpty()) {
                    JOptionPane.showMessageDialog(this, Utils.translate(ERROR_BAD_CONFIG, configuration.getName()));
                    return;
                }
            } else if (configuration.getType() == IOType.Map) {
                if (configuration.getMap().isEmpty()) {
                    JOptionPane.showMessageDialog(this, Utils.translate(ERROR_BAD_CONFIG, configuration.getName()));
                    return;
                }
            } else if (configuration.getType() == IOType.Date) {
                try {
                    SimpleDateFormat sdf = new SimpleDateFormat(configuration.getDateFormat());
                    sdf.format(new Date());
                } catch (Exception e) {
                    JOptionPane.showMessageDialog(this, Utils.translate(ERROR_BAD_CONFIG, configuration.getName()));
                    return;
                }
            }
        }
        if (outputConfiguration.getType() == IOType.Categorical) {
            if (outputConfiguration.getCategoricalList().isEmpty()) {
                JOptionPane.showMessageDialog(this, Utils.translate(ERROR_BAD_OUTPUT_CONFIG));
                return;
            }
        }
        int numberOfInputs = numberOfInputsSpinnerModel.getNumber().intValue();
        IOConfiguration[] temp = new IOConfiguration[numberOfInputs];
        System.arraycopy(inputConfigurations, 0, temp, 0, numberOfInputs);
        for (int i = 0; i < temp.length; i++) {
            temp[i].setName(inputConfigurations[i].getName());
        }
        outputConfiguration.setName(outputNameField.getText());
        ui.updateProjectDefinition(temp, outputConfiguration);
        dispose();
    }

    private void adjustSelectedInputName() {
        inputConfigurations[selectedInputCombo.getSelectedIndex()].setName(inputNameField.getText());
    }

    // Set the input names to something useful if this is the first time.
    private void defaultNames() {
        int numberOfInputs = numberOfInputsSpinnerModel.getNumber().intValue();
        for (int i = 0; i < numberOfInputs; i++) {
            if (inputConfigurations[i].getName() == null || inputConfigurations[i].getName().trim().length() == 0) {
                inputConfigurations[i].setName("Input " + (i + 1));
            }
        }
    }

    private void configureInput() {
        IOType type = (IOType) inputTypeCombo.getSelectedItem();
        int selectedInput = selectedInputCombo.getSelectedIndex();
        IOConfiguration configuration = inputConfigurations[selectedInput];
        if (type == IOType.Numeric) {
            EditNumericIODialog dialog = new EditNumericIODialog(this, configuration, getPreferences());
            dialog.setVisible(true);
        } else if (type == IOType.Map) {
            EditMapIODialog dialog = new EditMapIODialog(this, configuration, getPreferences());
            dialog.setVisible(true);
        } else if (type == IOType.Categorical) {
            EditCategoricalIODialog dialog = new EditCategoricalIODialog(this, configuration, getPreferences(), true);
            dialog.setVisible(true);
        } else if (type == IOType.Date) {
            EditDateIODialog dialog = new EditDateIODialog(this, configuration, getPreferences());
            dialog.setVisible(true);
        }
    }

    private void configureOutput() {
        IOType type = (IOType) outputTypeCombo.getSelectedItem();
        if (type == IOType.Numeric) {
            EditNumericIODialog dialog = new EditNumericIODialog(this, outputConfiguration, getPreferences());
            dialog.setVisible(true);
        } else if (type == IOType.Categorical) {
            EditCategoricalIODialog dialog = new EditCategoricalIODialog(this, outputConfiguration, getPreferences(),
                    false);
            dialog.setVisible(true);
        }
    }

}